package logx

import (
	"context"
	"fmt"
	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"github.com/rifflock/lfshook"
	"github.com/sirupsen/logrus"
	"log"
	"runtime"
	"strings"
	"time"
)

const skip = 2

type logrusAdapt struct {
	l          *logrus.Logger
	ctx        context.Context
	callerSkip int
}

func NewLogrusAdapt(cfg *LogConfig) ILogger {
	l := logrus.New()

	writer1, err := rotatelogs.New(
		cfg.LogSavePath+"/app-%Y%m%d.log",
		rotatelogs.WithLinkName(cfg.LogSavePath+"/app.log"),      //软连接，只想最新日志文件
		rotatelogs.WithMaxAge(7*24*time.Hour),                    //最大保存时间
		rotatelogs.WithRotationTime(time.Duration(24)*time.Hour), //日志分割时间间隔
	)

	if err != nil {
		l.Errorf("创建日志文件失败， %s, ", err.Error())
	}

	formatter := NewLogrusFormatter(cfg)

	lfHook := lfshook.NewHook(lfshook.WriterMap{
		logrus.DebugLevel: writer1,
		logrus.InfoLevel:  writer1,
		logrus.WarnLevel:  writer1,
		logrus.ErrorLevel: writer1,
		logrus.FatalLevel: writer1,
		logrus.PanicLevel: writer1,
	}, formatter)

	l.AddHook(lfHook)
	l.SetFormatter(formatter)

	adapt := &logrusAdapt{
		l: l,
	}

	adapt.SetOutputLevel(cfg.Level)
	return adapt
}

func NewLogrusFormatter(cfg *LogConfig) logrus.Formatter {
	switch strings.ToLower(cfg.Format) {
	case "json":
		return &logrus.JSONFormatter{}
	case "text":
		return &logrus.TextFormatter{}
	default:
		return &CustomLogFormatter{}
	}
}

func (s logrusAdapt) WithCallerSkip(skip int) ILogger {
	panic("implement me")
}

func (s logrusAdapt) WithFields(fields ...LogField) ILogger {
	panic("implement me")
}

func (s logrusAdapt) WithDuration(d time.Duration) ILogger {
	panic("implement me")
}

func (s logrusAdapt) WithContext(ctx context.Context) ILogger {
	s.ctx = ctx
	return s
}

func (s logrusAdapt) Info(args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Info(args...)
}

func (s logrusAdapt) Tracef(format string, args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Tracef(format, args...)
}

func (s logrusAdapt) Debugf(format string, args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Debugf(format, args...)
}

func (s logrusAdapt) Infof(format string, args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Infof(format, args...)
}

func (s logrusAdapt) Warnf(format string, args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Warnf(format, args...)
}

func (s logrusAdapt) Errorf(format string, args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Errorf(format, args...)
}

func (s logrusAdapt) Panicf(format string, args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Panicf(format, args...)
}

func (s logrusAdapt) Fatalf(format string, args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Fatalf(format, args...)
}

func (s logrusAdapt) Trace(args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Trace(args...)
}

func (s logrusAdapt) Debug(args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Debug(args...)
}

func (s logrusAdapt) Print(args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Print(args...)
}

func (s logrusAdapt) Warn(args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Warn(args...)
}

func (s logrusAdapt) Error(args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Error(args...)
}

func (s logrusAdapt) Panic(args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Panic(args...)
}

func (s logrusAdapt) Fatal(args ...interface{}) {
	pc, file, line, _ := runtime.Caller(skip)
	s.l.WithFields(logrus.Fields{"pc": pc, "file": file, "line": line}).Fatal(args...)
}

func (s logrusAdapt) SetOutputLevel(level string) {
	if level == "" {
		level = "info"
	}
	lev, err := logrus.ParseLevel(level)
	if err != nil {
		log.Fatalln(err)
	}
	s.l.SetLevel(lev)
}

type CustomLogFormatter struct {
}

func (l *CustomLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	level := entry.Level
	timestamp := entry.Time.Local().Format("2006-01-02 15:04:05.000")
	lev := strings.ToUpper(level.String())
	for 5-len(lev) > 0 {
		lev = lev + " "
	}
	logMsg := fmt.Sprintf("%s [%s]", timestamp, lev)

	var (
		file string
		line int
	)

	for k, v := range entry.Data {
		if k == "file" {
			file = v.(string)
		}
		if k == "line" {
			line = v.(int)
		}
	}

	logMsg = logMsg + fmt.Sprintf(" [%s:%d] %s\n", file, line, entry.Message)
	return []byte(logMsg), nil
}
